Published on

Chat Bot 实战

Authors
  • avatar
    Name
    游戏人生
    Twitter

实现步骤

  1. 首先定义一个简单的,根据 MBTI 类型和问题回答用户的 chain:
  const prompt = ChatPromptTemplate.fromMessages([
    [
      "system",
      "你是一个共情能力非常强的心理医生,并且很了解MBTI(迈尔斯-布里格斯性格类型指标)的各种人格类型,你的任务是根据来访者的 MBTI 和问题,给出针对性的情感支持,你的回答要富有感情、有深度和充足的情感支持,引导来访者乐观积极面对问题",
    ],
    ["human", "用户的 MBTI 类型是{type}, 这个类型的特点是{info}, 他的问题是{question}"],
  ]);

  const llm = new ChatAlibabaTongyi({
    model: "qwen-max",
  });
  const mbtiChain = RunnableSequence.from([prompt, model, new StringOutputParser()]);

这是非常简单的 chat chain,除了告诉 llm MBTI 相关的信息外,还给 llm 设定了 “引导来访者乐观积极面对问题” 这样有倾向性的 prompt,引导 prompt 的回答风格。

需要的参数:

  • type: 用户的 MBTI 类型
  • info: 对应 MBTI 的类型的特点描述。对于这种由固定答案的信息,不要让 llm 自己生成,而且由我们提供确定性的信息。
  • question: 用户的问题
  1. 据此定义一个自定义的 tool:
  const mbtiTool = new DynamicStructuredTool({
    name: "get-mbti-chat",
    schema: z.object({
      type: z.enum(mbtiList).describe("用户的 MBTI 类型"),
      question: z.string().describe("用户的问题"),
    }),
    func: async ({ type, question }) => {
      const info = mbtiInfo[type];

      const res = await mbtiChain.invoke({ type, question, info });
      return res;
    },
    description: "根据用户的问题和 MBTI 类型,回答用户的问题",
  });

其中,mbtiList 是穷举所有 MBTI 的类型。 mbtiInfo 是从外部 json 中加载出来的,以 mbti 类型为 key 对性格描述的 object。 这里把 type 和 question 定义为我们函数 schema 的必填的参数,方便 llm 对函数的理解。

  1. 据此创建一个 agents:
  const tools = [mbtiTool];

  const agentPrompt = await ChatPromptTemplate.fromMessages([
    [
      "system",
      "你是一个用户接待的 agent,通过自然语言询问用户的 MBTI 类型和问题,直到你有足够的信息调用 get-mbti-chat 来回答用户的问题",
    ],
    new MessagesPlaceholder("history_message"),
    ["human", "{input}"],
    new MessagesPlaceholder("agent_scratchpad"),
  ]);

  const llm = new ChatAlibabaTongyi({
    model: "qwen-max",
    temperature: 0.4,
  });

  const agent = await createOpenAIToolsAgent({
    llm,
    tools,
    prompt: agentPrompt,
  });

  const agentExecutor = new AgentExecutor({
    agent,
    tools,
  });

为了演示,只给 agents 添加了这一个 tool,并且在 system prompt 中强化了对 get-mbti-chat 的调用的指示。如果是在工程中,也不要加太多的 tool,可以采用 Route/RunnableBranch 等方案,由路由去引导用户到对应的专业 agents 去处理。

  1. 给 agents 添加 history:(也可以使用持久化在文件中的 chat history 解决方案)
  const messageHistory = new ChatMessageHistory();

  const agentWithChatHistory = new RunnableWithMessageHistory({
    runnable: agentExecutor,
    getMessageHistory: () => messageHistory,
    inputMessagesKey: "input",
    historyMessagesKey: "history_message",
  });
  1. 使用 nodejs 的 readline 内置库,迅速构建一个可以在 cli 中使用的 chat bot,方便进行测试:
  const rl = readline.createInterface({
    input: process.stdin,
    output: process.stdout,
  });

  function chat() {
    rl.question("User: ", async (input) => {
      if (input.toLowerCase() === "exit") {
        rl.close();
        return;
      }

      const response = await agentWithChatHistory.invoke(
        {
          input,
        },
        {
          configurable: {
            sessionId: "no-used",
          },
        }
      );

      console.log("Agent: ", response.output);

      chat();
    });
  }

  console.log("请输入问题。 输入 exit 退出聊天。");
  chat();

6.运行脚本,测试 chat bot。